#!/bin/sh
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

# NOTE: This agent has been adapted from the Checkmk linux agent.

inpath() {
    # replace "if type [somecmd]" idiom
    # 'command -v' tends to be more robust vs 'which' and 'type' based tests
    command -v "${1:?No command to test}" >/dev/null 2>&1
}

get_epoch() {
    date +%s 2>/dev/null ||
        perl -e 'print($^T."\n");'
}

# Remove locale settings to eliminate localized outputs where possible
# Author: Christian Zigotzky <info@xenosoft.de>
export LC_ALL=C
unset LANG

export MK_LIBDIR="/to/be/changed"
export MK_CONFDIR="/to/be/changed"

# Optionally set a tempdir for all subsequent calls
#export TMPDIR=

# close standard input (for security reasons) and stderr
if [ "$1" = -d ]; then
    set -xv
else
    exec </dev/null 2>/dev/null
fi

section_checkmk() {
    echo "<<<check_mk>>>"
    echo "Version: 2.5.0b1"
    echo "AgentOS: macosx"
    echo "Hostname: $(hostname)"
    echo "AgentDirectory: $MK_CONFDIR"
    echo "DataDirectory: $MK_VARDIR"
    echo "SpoolDirectory: $SPOOLDIR"
    echo "PluginsDirectory: $PLUGINSDIR"
    echo "LocalDirectory: $LOCALDIR"
    echo "OSName: $(sw_vers -productName)"
    echo "OSVersion: $(sw_vers -productVersion)"
    echo "OSType: macos"
}

section_lnx_if() {
    echo '<<<lnx_if:sep(58)>>>'

    netstat -inb | grep -E '^en|^lo' | grep Link | sed -e 's/[*]//g' | awk '{ print $1": "$7,$5,$6,"0","0","0","0","0",$10,$8,$9,"0","0",$11,"0","0"; }'

    printf "[lo0]\n Link detected: yes\n"
    for ifname in $(netstat -inb | sed -e 's/[*]//g' | grep en | grep Link | awk '{print $1;}'); do
        IFSPEED='Unknown!'
        IFDUPLEX="Half"
        if ifconfig "${ifname}" | grep -q "media: autoselect"; then
            IFAUTO="on"
        else
            IFAUTO="off"
        fi
        if ifconfig "${ifname}" | grep -q "full-duplex"; then
            IFDUPLEX="Full"
        fi
        if ifconfig "${ifname}" | grep -q "status: active"; then
            IFLINK="yes"
        else
            IFLINK="no"
        fi
        if ifconfig "${ifname}" | grep -Eq '[0-9]+baseT'; then
            IFSPEED=$(ifconfig "${ifname}" | grep -Eo '[0-9]+baseT' | sed -e 's@baseT@Mb/s@')
        fi
        echo "[${ifname}]"
        echo " Speed: $IFSPEED"
        echo " Duplex: $IFDUPLEX"
        echo " Auto-negotiation: $IFAUTO"
        echo " Link detected: $IFLINK"
    done
}

section_df() {
    echo '<<<df>>>'
    # SC2162: read without -r will mangle backslashes.
    # The following suppression was added when we enabled the corresponding shellcheck.
    # It may well be that "read -r" would be more appropriate.
    # shellcheck disable=SC2162
    df -kPT hfs,apfs | sed 1d |
        while read DEV REST; do
            TYPE=$(diskutil info "$DEV" | grep '^\s*Type' | cut -d: -f2 | tr -d '[:space:]')
            echo "$DEV $TYPE $REST"
        done
}

section_cpu() {
    echo '<<<cpu>>>'
    echo "$(sysctl -n vm.loadavg | tr -d '{}') $(top -l 1 -n 1 | grep -E ^Processes: |
        awk '{ print $4"/"$2; }') $(echo 'echo $$' | bash) $(sysctl -n hw.ncpu)"
}

section_mem() {
    echo '<<<mem>>>'
    echo "MemTotal: $(echo "$(sysctl -n hw.memsize)/1024" | bc) kB"
    echo "MemFree: $(echo "( $(vm_stat | grep speculative: | awk '{print $3}') + $(vm_stat | grep inactive: | awk '{print $3}') + $(vm_stat | grep free: | awk '{print $3}') ) * $(vm_stat | grep Mach | awk '{print $8}') / 1024" | bc) kB"
    echo "SwapTotal: 0 kB"
    echo "SwapFree: 0 kB"
    # FIXME: Just call vm_stat here, write a check plugin that uses that
    # navite output of vm_stat
}

section_uptime() {
    echo '<<<uptime>>>'
    echo "$(get_epoch) - $(sysctl -n kern.boottime | cut -d' ' -f 4,7 | tr ',' '.' |
        tr -d ' ')" | bc
    # FIXME: use sysctl -a for outputting *all* kernel values. Write
    # checks plugins with subchecks for parsing that output. Maybe reduce
    # the output size by grepping away totally useless parts
}

section_netctr() {
    echo '<<<netctr>>>'
    date +'%s'
    netstat -inb | grep -v -E '(^Name|lo|plip)' | grep Link | awk '{
    print $1,$7,$5,$6,"0","0","0","0","0",$10,$8,$9,"0","0",$11,"0","0"; }'
    # FIXME: send netstat -inb plain, write proper check plugins for
    # clean parsing of the output
}

section_ps() {
    echo '<<<ps>>>'
    echo "[time]"
    get_epoch
    echo "[processes]"
    ps ax -o user,vsz,rss,pcpu,command | sed -e 1d -e 's/ *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) */(\1,\2,\3,\4) /'
}

section_ntp() {
    # NTP seems to be enabled as a default
    if inpath ntpq; then
        echo '<<<ntp>>>'
        ntpq -np | sed -e 1,2d -e 's/^\(.\)/\1 /' -e 's/^ /%/'
    fi
}

section_tcp_conn_stats() {
    # TCP Conn stats
    echo '<<<tcp_conn_stats>>>'
    netstat -ntfinet | awk ' /^tcp/ { c[$6]++; } END { for (x in c) { print x, c[x]; } }'
}

section_fileinfo() {
    # Fileinfo-Check: put patterns for files into /etc/check_mk/fileinfo.cfg
    perl -e '
    use File::Glob "bsd_glob";
    my @patterns = ();
    foreach (bsd_glob("$ARGV[0]/fileinfo.cfg"), bsd_glob("$ARGV[0]/fileinfo.d/*")) {
        open my $handle, "<", $_ or next;
        while (<$handle>) {
            chomp;
            next if /^\s*(#|$)/;
            my $pattern = $_;
            $pattern =~ s/\$DATE:(.*?)\$/substr(`date +"$1"`, 0, -1)/eg;
            push @patterns, $pattern;
        }
        warn "error while reading $_: $!\n" if $!;
        close $handle;
    }
    exit if ! @patterns;

    my $file_stats = "";
    foreach (@patterns) {
        foreach (bsd_glob("$_")) {
            if (! -f) {
                $file_stats .= "$_|missing\n" if ! -d;
            } elsif (my @infos = stat) {
                $file_stats .= "$_|ok|$infos[7]|$infos[9]\n";
            } else {
                $file_stats .= "$_|stat failed: $!\n";
            }
        }
    }

    print "<<<fileinfo:sep(124)>>>\n", time, "\n[[[header]]]\nname|status|size|time\n[[[content]]]\n$file_stats";
    ' -- "${MK_CONFDIR}"

}

section_timemachine() {
    if type tmutil >/dev/null; then
        echo '<<<timemachine>>>'
        tmutil latestbackup 2>&1
    fi
}

run_purely_synchronous_sections() {

    section_checkmk

    section_lnx_if

    section_df

    section_cpu

    section_mem

    section_uptime

    section_netctr

    section_ps

    section_ntp

    section_tcp_conn_stats

    section_fileinfo

    section_timemachine

}

###############################
# Things up for takers:
###############################
# *OSX SW Raid status
# *launchctl daemon status
# *hw sensors, how to query them?
# *OSX Server specific stuff, LDAP, etc...
# *Rewrite cpu / ps check to be faster - takes >1s on my laptop
# ioreg -l zeigt etliche interessante Inventurdaten

_non_comment_lines() {
    grep -Ev '^[[:space:]]*($|#)' "${1}"
}

run_mrpe_plugins() {
    configfile="${1}"

    if [ -e "/etc/mrpe.cfg" ]; then
        echo '<<<mrpe>>>'
        # SC2162: read without -r will mangle backslashes.
        # The following suppression was added when we enabled the corresponding shellcheck.
        # It may well be that "read -r" would be more appropriate.
        # shellcheck disable=SC2162
        _non_comment_lines "${configfile}" | while read descr cmdline; do
            PLUGIN=${cmdline%% *}
            OUTPUT=$(eval "$cmdline")
            echo "(${PLUGIN##*/}) $descr $? $OUTPUT" | tr \\n \\1
            echo
        done
    fi
}

main() {

    run_purely_synchronous_sections

    run_mrpe_plugins "/etc/mrpe.cfg"

    return 0
}

[ -n "${MK_SOURCE_AGENT}" ] || main "$@"
